#include "kvdb/client/kvdbclient.h"
#include "vzconn/conn/vzclient.h"

namespace kvdb {

static const char* VZ_HEADER = "VZ";

KvdbClient::KvdbClient()
  : write_buffer_(new vzconn::ByteBuffer()) {
}

KvdbClient::~KvdbClient() {
}

bool KvdbClient::InitKvdbClient(const char *server, uint16 port) {
  server_addr_ = server;
  server_port_ = port;
  block_connect_ = vzconn::VzClient::ConnectBlockIpcServer(
                     io_service_,
                     server,
                     port);
  if(block_connect_) {
    return true;
  }
  return false;
}

void KvdbClient::UinitKvdbClient() {
  if(block_connect_) {
    block_connect_->CloseConnect();
  }
}

bool KvdbClient::CheckConnected() {
  if(block_connect_ && block_connect_->IsConnected()) {
    return true;
  }
  LOG(WARNING) << "Socket disconnect, try to reconnect";
  return InitKvdbClient(server_addr_.c_str(), server_port_);
}

bool KvdbClient::SetKey(const std::string key,
                        const char *value, uint32 value_size) {
  return SetKey(key.c_str(), key.size(), value, value_size);
}
bool KvdbClient::SetKey(const char *key, uint8 key_size,
                        const char *value, uint32 value_size) {
  try {
    if(!CheckConnected()) {
      LOG(ERROR) << "Socket disconnect";
      return false;
    }
    bool res = 0;
    // Write Requestion
    vzconn::ByteBuffer::Ptr buffer = PrepareBuffer(KVDB_REPLACE,
                                     key, key_size,
                                     value, value_size);

    res = block_connect_->SyncWrite(buffer->Data(), buffer->size());
    if(!res) {
      LOG(ERROR) << "SyncWrite data error";
      return false;
    }

    // Read Data
    res = block_connect_->SyncRead(read_buffer_);
    if(!res) {
      LOG(ERROR) << "SyncRead data error";
      return false;
    }
    if(read_buffer_.size() == 1 && read_buffer_[0] == KVDB_RESULT_SUCCEED) {
      ReplaceBufferKeyValue(std::string(key, key_size),
                            std::string(value, value_size));
      return true;
    }
  } catch (std::exception &e) {
    LOG(ERROR) << e.what();
  }
  return false;
}


bool KvdbClient::GetKey(const std::string key,
                        std::string *result,
                        bool absolute) {
  return GetKey(key.c_str(), key.size(), result, absolute);
}


bool KvdbClient::GetKey(const std::string key,
                        void *buffer,
                        std::size_t buffer_size,
                        bool absolute) {
  std::string result;
  bool res = GetKey(key.c_str(), key.size(), &result, absolute);
  if(res == false || result.size() != buffer_size) {
    return false;
  }
  memcpy(buffer, result.c_str(), buffer_size);
  return true;
}

bool KvdbClient::GetKey(const char *key, uint8 key_size,
                        void *buffer,
                        std::size_t buffer_size,
                        bool absolute) {
  return GetKey(std::string(key, key_size), buffer, buffer_size, absolute);
}

bool KvdbClient::GetKey(const char *key, uint8 key_size,
                        std::string *result,
                        bool absolute) {
  try {
    if(!absolute) {
      std::string key_string(key, key_size);
      std::map<std::string, std::string>::iterator iter =
        key_values_.find(key_string);
      if(iter != key_values_.end()) {
        *result = iter->second;
        return true;
      }
    }
    if(!CheckConnected()) {
      LOG(ERROR) << "Socket disconnect";
      return false;
    }
    // Write Requestion
    vzconn::ByteBuffer::Ptr buffer = PrepareBuffer(KVDB_SEARCH,
                                     key, key_size,
                                     NULL, 0);

    bool res = 0;
    res = block_connect_->SyncWrite(buffer->Data(), buffer->size());
    if(!res) {
      LOG(ERROR) << "SyncWrite data error";
      return false;
    }

    // Read Data
    res = block_connect_->SyncRead(read_buffer_);
    if(!res) {
      LOG(ERROR) << "SyncRead data error";
      return false;
    }

    res = ParseGetKey((const char *)&read_buffer_[0],
                      read_buffer_.size(),
                      result);
    if (res && !result->empty()) {
      ReplaceBufferKeyValue(std::string(key, key_size), *result);
      return true;
    }
  } catch (std::exception &e) {
    LOG(ERROR) << e.what();
  }
  return false;
}

bool KvdbClient::DeleteKey(const char *key, uint8 key_size) {
  static std::string result;
  try {
    if(!CheckConnected()) {
      LOG(ERROR) << "Socket disconnect";
      return false;
    }
    // Write Requestion
    DeleteBufferKeyValue(std::string(key, key_size));
    vzconn::ByteBuffer::Ptr buffer = PrepareBuffer(KVDB_DELETE,
                                     key, key_size,
                                     NULL, 0);

    bool res = 0;
    res = block_connect_->SyncWrite(buffer->Data(), buffer->size());
    if(!res) {
      LOG(ERROR) << "SyncWrite data error";
      return false;
    }

    // Read Data
    res = block_connect_->SyncRead(read_buffer_);
    if(!res) {
      LOG(ERROR) << "SyncRead data error";
      return false;
    }

    if(read_buffer_.size() == 1 && read_buffer_[0] == KVDB_RESULT_SUCCEED) {
      return true;
    }
  } catch (std::exception &e) {
    LOG(ERROR) << e.what();
  }
  return false;
}

vzconn::ByteBuffer::Ptr KvdbClient::PrepareBuffer(
  uint8 type,
  const char *key,
  uint8 key_size,
  const char *value,
  uint32 value_size) {
  write_buffer_->Clear();
  BOOST_ASSERT(write_buffer_->size() == 0);
  write_buffer_->WriteUInt8(type);
  write_buffer_->WriteUInt8(key_size);
  write_buffer_->WriteUInt32(value_size);
  write_buffer_->WriteBytes(key, key_size);
  if(value_size) {
    write_buffer_->WriteBytes(value, value_size);
  }
  return write_buffer_;
}

void KvdbClient::ReplaceBufferKeyValue(const std::string key,
                                       const std::string value) {
  std::map<std::string, std::string>::iterator iter = key_values_.find(key);
  if(iter != key_values_.end()) {
    iter->second = value;
  } else {
    key_values_.insert(std::pair<std::string, std::string>(key, value));
  }
}

void KvdbClient::DeleteBufferKeyValue(const std::string key) {
  std::map<std::string, std::string>::iterator iter = key_values_.find(key);
  if(iter != key_values_.end()) {
    key_values_.erase(iter);
  }
}

bool KvdbClient::ParseGetKey(const char *data, int size, std::string *result) {
  const char *buffer = data;
  std::size_t buffer_size = size;
  int offset = 0;
  if(buffer_size == 0) {
    LOG(ERROR) << "The Buffer size error";
    return false;
  }
  // Read the result
  uint8 res = buffer[offset];
  offset ++;
  if(res == KVDB_RESULT_FAILURE) {
    if(buffer_size != 1) {
      return false;
    } else {
      return true;
    }
  }

  if((buffer_size - offset) < sizeof(uint32)) {
    return false;
  }

  uint32 value_size = 0;
  memcpy(&value_size, buffer + offset, sizeof(uint32));
  value_size = ntohl(value_size);
  offset += sizeof(uint32);
  if((buffer_size - offset) == value_size) {
    result->append(buffer + offset, buffer_size - offset);
    return true;
  }
  return false;
}
}
